package com.openx.xframework.common.controller;

import com.openx.xframework.common.SQLOrderDirection;
import com.openx.xframework.common.exception.ParameterErrorException;
import com.openx.xframework.common.model.BaseModel;
import com.openx.xframework.common.model.DataTablePage;
import com.openx.xframework.common.service.BaseCRUDService;
import com.openx.xframework.common.utility.JacksonUtil;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

/**
 * 符合REST通用的CRUD Controller
 * 
 *
 * @param <PK>
 *            模型的主键类型
 * @param <M>
 *            模型model
 * @param <S>
 *            服务Service
 */
public abstract class BaseCRUDController<PK extends Serializable, M extends BaseModel<PK>, S extends BaseCRUDService<PK, M>> extends BaseController {
	protected final Logger logger = LoggerFactory.getLogger(this.getClass());// 日志
	protected S service = null;// 继承BaseService的服务类,提供对DAO进行通用的CRUD操作.
	protected Class<M> entityClass = null;// 保存Model的类型,以便转json等用途

	/**
	 * 构造函数 获取泛型的类(Class)
	 */
	public BaseCRUDController() {
		Type genType = getClass().getGenericSuperclass();
		Type[] paramTypes = ((ParameterizedType) genType).getActualTypeArguments();
		entityClass = (Class<M>) paramTypes[1];
	}
	/**
	 * 新增, 成功反馈201 created状态
	 *
	 * @param bean
	 *            新增的模型实体
	 * @return
	 */
	@RequestMapping(method = RequestMethod.POST)
	@ResponseStatus(HttpStatus.CREATED)
	@ApiOperation(value = "新增对象", notes = "返回主键ID,需要Mapper配置支持.")
	@ApiResponses(value = { @ApiResponse(code = 201, message = "新增成功"), @ApiResponse(code = 400, message = "参数错误") })
	public PK add(@ApiParam(value = "新增对象", required = true) @Valid @RequestBody M bean, Errors errors) {
		if (errors.hasErrors()) { throw new ParameterErrorException(errors.getFieldErrors()); }
		return this.service.add(bean);
	}
	

	/**
	 * 编辑模型实体,修改成功反馈204 NO CONTENT状态
	 *
	 * @param bean
	 *            编辑后的模型实体
	 * @return
	 */
	@RequestMapping(method = RequestMethod.PUT)
	@ResponseStatus(HttpStatus.NO_CONTENT)
	@ApiOperation(value = "编辑对象", notes = "返回修改的条目数量")
	@ApiResponses(value = { @ApiResponse(code = 204, message = "编辑成功"), @ApiResponse(code = 400, message = "参数错误") })
	public Integer edit(@ApiParam(value = "编辑对象", required = true) @Valid @RequestBody M bean, Errors errors) {
		if (errors.hasErrors()) { throw new ParameterErrorException(errors.getFieldErrors()); }
		return service.edit(bean);
	}

	/**
	 * 删除模型实体操作,删除成功反馈204 No Content状态.
	 *
	 * @param id(Integer)
	 *            需要删除的模型实体ID
	 * @return
	 */
	@RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
	@ResponseStatus(HttpStatus.NO_CONTENT)
	@ApiOperation(value = "删除对象", notes = "返回删除的条目数量")
	@ApiResponse(code = 204, message = "删除成功")
	public Integer delete(@ApiParam(value = "主键", required = true,defaultValue="10") @PathVariable("id") PK id) {
		return service.remove(id);
	}

	/**
	 * 通过ID获取单个模型实体,一般用于编辑的前置动作.
	 *
	 * @param id(String)
	 *            模型实体的主键ID
	 * @return 反馈JSON格式模型实体
	 */
	@RequestMapping(value = "/{id}", method = RequestMethod.GET)
	@ResponseStatus(HttpStatus.OK)
	@ApiOperation(value = "通过ID获取对象")
	@ApiResponse(code = 200, message = "获取成功")
	public ResponseEntity<M> get(@ApiParam(value = "主键", required = true,defaultValue="1") @PathVariable("id") PK id) {
		M bean = service.get(id);
		return new ResponseEntity<M>(bean, HttpStatus.OK);
	}

	/**
	 * 符合DataTable参数结构的搜索操作.
	 *
	 * @param draw
	 * @param length
	 * @param start
	 * @param searchInfoStr
	 */
	@RequestMapping(value = "/{start}/{length}", method = RequestMethod.GET)
	@ResponseStatus(HttpStatus.OK)
	@ApiOperation(value = "分页查询", notes = "格式为dataTable")
	@ApiResponses(value = { @ApiResponse(code = 200, message = "查询成功") })
	public DataTablePage<PK, M> list(
			@ApiParam(value = "页大小", required = true,defaultValue="10") @PathVariable("length") Integer length,
			@ApiParam(value = "页码", required = true,defaultValue="1") @PathVariable("start") Integer start,
			@ApiParam(value = "搜索条件", required = false) @RequestParam(value = "search", required = false) String searchInfoStr) throws Exception {

		M condition = JacksonUtil.readValue(searchInfoStr, entityClass);
		DataTablePage<PK, M> page = this.service.page(condition, start, length, "id", SQLOrderDirection.ASC);
		return page;
	}

	public S getService() {
		return service;
	}

	@Autowired
	public void setService(S service) {
		this.service = service;
	}
}
